package com.sai.javafx.threedimensional;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.TimelineBuilder;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Point3D;
import javafx.scene.GroupBuilder;
import javafx.scene.Parent;
import javafx.scene.PerspectiveCameraBuilder;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.PolygonBuilder;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.RotateBuilder;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @reference: http://sett.ociweb.com/sett/settMay2012.html
 */
public class Tetrahedron extends Application {
    public static final double DIHEDRAL_ANGLE = 70.53;
    public static final double INRADIUS = 50 * Math.tan(DIHEDRAL_ANGLE / 2 / 180 * Math.PI);

    private DoubleProperty side2Angle = new SimpleDoubleProperty();
    private DoubleProperty side3Angle = new SimpleDoubleProperty();
    private DoubleProperty side4Angle = new SimpleDoubleProperty();
    private DoubleProperty rootAngle = new SimpleDoubleProperty();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        stage.setTitle("Tetrahedron");
        stage.setScene(makeScene());
        stage.show();
        animate();
    }

    private Scene makeScene() {
        return SceneBuilder.create()
            .width(500)
            .height(500)
            .root(createRoot())
            .camera(PerspectiveCameraBuilder.create()
                .build())
            .depthBuffer(true)
            .build();
    }

    private Parent createRoot() {
        double x[] = new double[3];
        double y[] = new double[3];
        double angle = 2 * Math.PI / 3;
        for (int i = 0; i < 3; i++) {
            x[i] = 100 * Math.cos(i * angle);
            y[i] = 100 * Math.sin(i * angle);
        }

        final Polygon side1 = PolygonBuilder.create()
            .points(x[0], y[0], x[1], y[1], x[2], y[2])
            .fill(Color.RED)
            .opacity(0.9)
            .build();

        final Rotate side2Rotate = RotateBuilder.create()
            .pivotX(x[0])
            .pivotY(y[0])
            .pivotZ(0)
            .axis(new Point3D(x[0] - x[1], y[0] - y[1], 0))
            .build();
        side2Rotate.angleProperty().bind(side2Angle);

        final Polygon side2 = PolygonBuilder.create()
            .points(x[0], y[0], x[1], y[1], x[2], y[2])
            .fill(Color.GREEN)
            .opacity(0.9)
            .transforms(side2Rotate)
            .build();

        final Rotate side3Rotate = RotateBuilder.create()
            .pivotX(x[1])
            .pivotY(y[1])
            .pivotZ(0)
            .axis(new Point3D(x[1] - x[2], y[1] - y[2], 0))
            .build();
        side3Rotate.angleProperty().bind(side3Angle);

        final Polygon side3 = PolygonBuilder.create()
            .points(x[0], y[0], x[1], y[1], x[2], y[2])
            .fill(Color.BLUE)
            .opacity(0.9)
            .transforms(side3Rotate)
            .build();

        final Rotate side4Rotate = RotateBuilder.create()
            .pivotX(x[2])
            .pivotY(y[2])
            .pivotZ(0)
            .axis(new Point3D(x[2] - x[0], y[2] - y[0], 0))
            .build();
        side4Rotate.angleProperty().bind(side4Angle);

        final Polygon side4 = PolygonBuilder.create()
            .points(x[0], y[0], x[1], y[1], x[2], y[2])
            .fill(Color.CYAN)
            .opacity(0.9)
            .transforms(side4Rotate)
            .build();


        final Rotate rootRotate = RotateBuilder.create()
            .pivotX(0)
            .pivotY(0)
            .pivotZ(-INRADIUS)
            .axis(new Point3D(1, 1, 1))
            .build();
        rootRotate.angleProperty().bind(rootAngle);

        return GroupBuilder.create()
            .children(side1, side2, side3, side4)
            .translateX(250)
            .translateY(250)
            .translateZ(-INRADIUS)
            .transforms(rootRotate)
            .build();
    }

    private void animate() {
        TimelineBuilder.create()
            .keyFrames(
                new KeyFrame(
                    Duration.seconds(0),
                    new KeyValue(side2Angle, 0),
                    new KeyValue(side3Angle, 0),
                    new KeyValue(side4Angle, 0),
                    new KeyValue(rootAngle, -60)
                ),
                new KeyFrame(
                    Duration.seconds(2),
                    new KeyValue(side2Angle, 0),
                    new KeyValue(side3Angle, 0),
                    new KeyValue(side4Angle, DIHEDRAL_ANGLE),
                    new KeyValue(rootAngle, 60)
                ),
                new KeyFrame(
                    Duration.seconds(4),
                    new KeyValue(side2Angle, 0),
                    new KeyValue(side3Angle, DIHEDRAL_ANGLE),
                    new KeyValue(side4Angle, DIHEDRAL_ANGLE),
                    new KeyValue(rootAngle, 180)
                ),
                new KeyFrame(
                    Duration.seconds(6),
                    new KeyValue(side2Angle, DIHEDRAL_ANGLE),
                    new KeyValue(side3Angle, DIHEDRAL_ANGLE),
                    new KeyValue(side4Angle, DIHEDRAL_ANGLE),
                    new KeyValue(rootAngle, 300)
                )
            )
            .build().play();
    }
}
